package booksManagement;


import java.io.Serializable;
import java.util.Calendar;
import java.util.GregorianCalendar;

import exceptions.*;
import loansManagement.Loan;
import loansManagement.LoanStatus;
import main.Constraints;

import javax.persistence.*;

import org.hibernate.annotations.Type;

import subscribersManagement.Subscriber;

/**
 * @author        user
 */
@Entity
@Table(name = "copies")
@NamedQuery(name="findAllCopies", query="select c from Copy c")
public class Copy implements Serializable
{
	/**
   * 
   */
  private static final long serialVersionUID = 1L;
  private static long copyKey=1;
	/**
   * id number of the exemplary
   * @uml.property  name="number"
   */
	@Id
	@Column(name= "Id_number")
	//@GeneratedValue(strategy = GenerationType.AUTO)
	private long copyId;
	/**
   * @uml.property  name="media"
   * @uml.associationEnd  
   */
	@ManyToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "media_id")
	private Media media;
	
	private Calendar reservationLimit;
	
	@ManyToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "reserver_id")
	private Subscriber reservedBy=null;
	
	public Copy(){
		
	}
	/**
	 * create a new copy of the media
	 * @param media
	 * @throws BadParametersException
	 */
	public Copy(Media media) throws BadParametersException{
		if (media==null) throw new BadParametersException();
		this.media=media;
		this.copyId=copyKey;
		copyKey++;
	}
	
	/**
   * @return
   * @uml.property  name="number"
   */
	public long getNumber(){
  	return this.copyId;
  }
	/**
   * @param  number
   * @uml.property  name="number"
   */
	public void setCopyId(int number){
  	this.copyId = number;
  }
	
	/**
   * @return
   * @uml.property  name="media"
   */
	public Media getMedia(){
  	return media;
  }

	/**
   * @param  media
   * @uml.property  name="media"
   */
	public void setMedia(Media media){
  	this.media = media;
  }
	/**
	 * Two Copies are the same if their number and media are the same
	 */
	@Override
	public boolean equals(Object copy){
		if (copy == null)
			return false;
		Copy c = (Copy) copy;

		if (c.copyId == copyId && c.media.equals(media))
			return true;

		return false;
	}
	/**
	 * return True if this media is lent
	 * @return
	 */
	@Column(name="isLent", columnDefinition = "tinyint", nullable = false)
	@Type(type="yes_no")
	public boolean isLent() {
		if(copyLoan!=null)
			return copyLoan.getStatus()== LoanStatus.LOANED;
		else
			return false;
	}
	/**
	 * 
	 * @param loan
	 * @throws LentBookException
	 * @throws ReservedCopyException 
	 */
	public void lend(Loan loan) throws LentBookException, ReservedCopyException {
		// Check if the copy is available
		if (this.isLent())
			throw new LentBookException("Copy already lent.");
		if(reservedBy==null || reservedBy.equals(loan.getLender()) || reservationLimit.before(Calendar.getInstance())){
			this.copyLoan = loan;
			media.nbLentCopies++;
			reservedBy=null;
			reservationLimit=null;
		}else{
			throw new ReservedCopyException("This copy is currently reserved");
		}


	}
	
	public void returnCopy() throws LentBookException, BadParametersException {

		if (!isLent())
			throw new LentBookException("The copy is not lent");

		copyLoan.returnCopy();
		this.copyLoan = null;
		media.nbLentCopies--;
		if(this.media.getReservers().size()>0){
			reservedBy=this.media.getReservers().remove(0);
			Calendar rLimit = new GregorianCalendar();
			rLimit.add(Calendar.WEEK_OF_YEAR, Constraints.RESERVATION_DELAY);
			this.reservationLimit=rLimit;
			//reservedBy.getReservedCopies().add(this);
		}
	}
	
	@Override
	public String toString(){
		return "Copy number " + this.copyId + " of media " + this.media.getTitle();
	}

	/**
   * @uml.property  name="copyLoan"
   * @uml.associationEnd  
   */
	@OneToOne(mappedBy = "copy")
	//@PrimaryKeyJoinColumn
  private Loan copyLoan = null;

	/**
   * Getter of the property <tt>copyLoan</tt>
   * @return    Returns the copyLoan.
   * @uml.property  name="copyLoan"
   */
  public Loan getCopyLoan()
  {
	  return copyLoan;
  }
	/**
   * Setter of the property <tt>copyLoan</tt>
   * @param copyLoan    The copyLoan to set.
   * @uml.property  name="copyLoan"
   */
  public void setCopyLoan(Loan copyLoan)
  {
	  this.copyLoan = copyLoan;
  }
	public Subscriber getReservedBy()
  {
	  return reservedBy;
  }
	public void setReservedBy(Subscriber reservedBy)
  {
	  this.reservedBy = reservedBy;
  }

	
	

}
